package com.qys.livesMall.file.server.upload.service.impl.local;

import com.qys.livesMall.common.exception.QysException;
import com.qys.livesMall.file.server.upload.service.UploadFileService;
import com.qys.livesMall.file.server.upload.service.impl.local.proper.LocalStorageProperties;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.nio.charset.StandardCharsets;

/**
 * Created by IntelliJ IDEA.
 * User: dp
 * Date: 2023/05/19
 * Time: 14:25
 * DESCRIPTION: 本地文件上传
 */
@SuppressWarnings("ThrowFromFinallyBlock")
@Slf4j
@Component
public class LocalUploadFileServiceImpl extends UploadFileService {

    /**
     * 文件上传
     *
     * @param file     文件流
     * @param filePath 文件路径前缀
     * @param fileName 文件名
     */
    @Override
    public void uploadFile(MultipartFile file, String filePath, String fileName) {
        //文件路径
        File folder = super.checkFolder(filePath);

        //保存文件信息
        super.uploadFileToLocal(file, folder + "/" + fileName);
    }

    /**
     * 获取文件信息
     *
     * @param fileName 文件名
     * @param response
     * @param request
     */
    @Override
    public void getLocalFile(String fileName, HttpServletResponse response, HttpServletRequest request) {
        String filePath = LocalStorageProperties.localFilePath + fileName;

        // 获取图片文件流
        File file = new File(filePath);

        // 获取响应头
        String contentType = getContentType(file.getName());

        //图片
        if (contentType.contains("jpeg")) {
            writeImage(response, file, contentType);
        }
        //视频
        else {
            writeVideo(response, request, file, contentType, fileName);
        }
    }

    /**
     * 输出图片信息
     *
     * @param response
     * @param contentType
     * @throws IOException
     */
    private static void writeImage(HttpServletResponse response, File imageFile, String contentType) {
        InputStream inputStream = null;
        OutputStream outputStream = null;

        try {
            //获取文件输入流
            inputStream = new FileInputStream(imageFile);
            //获取输出流
            outputStream = response.getOutputStream();

            //设置请求头
            response.setContentType(contentType);
            response.setContentLengthLong(imageFile.length());

            // 将信息写出
            byte[] buffer = new byte[1024];
            int len;

            while ((len = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, len);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if (null != inputStream) {
                    inputStream.close();
                }
                if (null != outputStream) {
                    outputStream.close();
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * 返回视频信息
     *
     * @param response
     * @param request
     * @param file
     * @param contentType
     * @param fileName
     */
    @SuppressWarnings("resource")
    private void writeVideo(HttpServletResponse response, HttpServletRequest request, File file, String contentType,
                            String fileName) {
        //视频资源存储信息
        response.reset();
        //获取从那个字节开始读取文件
        String rangeString = request.getHeader("Range");
        try {
            //获取响应的输出流
            OutputStream outputStream = response.getOutputStream();

            if (file.exists()) {
                log.info("getVideo获取视频资源:{},读取文件字节:{}", file.getName(), rangeString);

                RandomAccessFile targetFile = new RandomAccessFile(file, "r");
                long fileLength = targetFile.length();

                //播放
                if (rangeString != null) {
                    long range = Long.parseLong(rangeString.substring(rangeString.indexOf("=") + 1, rangeString.indexOf("-")));
                    //设置内容类型
                    response.setHeader("Content-Type", contentType);
                    //设置此次相应返回的数据长度
                    response.setHeader("Content-Length", String.valueOf(fileLength - range));
                    //设置此次相应返回的数据范围
                    response.setHeader("Content-Range", "bytes " + range + "-" + (fileLength - 1) + "/" + fileLength);
                    //返回码需要为206，而不是200
                    response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
                    //设定文件读取开始位置（以字节为单位）
                    targetFile.seek(range);
                    // 每次请求只返回1MB的视频流
                    byte[] bytes = new byte[1024 * 1024];
                    int len = targetFile.read(bytes);
                    //设置此次相应返回的数据长度
                    response.setContentLength(len);
                    // 将这1MB的视频流响应给客户端
                    outputStream.write(bytes, 0, len);
                }
                //下载
                else {
                    //设置响应头，把文件名字设置好
                    response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
                    //设置文件长度
                    response.setHeader("Content-Length", String.valueOf(fileLength));
                    //解决编码问题
                    response.setHeader("Content-Type", "application/octet-stream");
                }
            }
            //文件未找到
            else {
                String message = "file:" + fileName + " not exists";
                //解决编码问题
                response.setHeader("Content-Type", "application/json");
                outputStream.write(message.getBytes(StandardCharsets.UTF_8));
            }
            outputStream.flush();
            outputStream.close();
        } catch (Exception e) {
            log.error("视频获取出错", e);
            throw new QysException(e.getMessage());
        }
    }

    private String getContentType(String fileName) {
        String extension = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();
        return switch (extension.toLowerCase()) {
            case "mp4" -> "video/mp4";
            case "ogg" -> "video/ogg";
            case "webm" -> "video/webm";
            default -> "image/jpeg";
        };
    }

    /**
     * 添加视频文件到视频库
     *
     * @param file      文件流
     * @param videoPath 文件路径
     * @param imgPath   图片保存地址
     * @param type      上传类型
     */
    @Async
    @Override
    public void videoFirstFrames(MultipartFile file, String videoPath, String imgPath, String type) {
        String imageName = super.saveVideoFirstFrames(LocalStorageProperties.localFilePath + videoPath, imgPath);
        if (null != imageName) {
            //更新图片地址到数据库
            super.updateImageUrlToVideoStore(videoPath, imgPath + imageName, type);
        }
    }
}
